 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.views.markers.internal;

 import java.util.ArrayList ;
 import java.util.Arrays ;
 import java.util.Collection ;
 import java.util.Collections ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Set ;
 import java.util.StringTokenizer ;

 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.resources.mapping.ResourceMapping;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.dialogs.IDialogSettings;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.IWorkingSet;
 import org.eclipse.ui.PlatformUI;

 /**
  * MarkerFilter is the class that defines a filter on markers in a
  * MarkerView.
  *
  */
 public class MarkerFilter implements Cloneable {

     static final String TAG_ENABLED = "enabled"; //$NON-NLS-1$

     private static final String TAG_ON_RESOURCE = "onResource"; //$NON-NLS-1$

     private static final String TAG_SELECTED_TYPES = "selectedType"; //$NON-NLS-1$

     private static final String TAG_WORKING_SET = "workingSet"; //$NON-NLS-1$

     private static final String TAG_TYPES_DELIMITER = ":"; //$NON-NLS-1$

     /**
      * New attribute to handle the selection status of marker types.
      */
     private static final String TAG_SELECTION_STATUS = "selectionStatus"; //$NON-NLS-1$

     /**
      * Attribute status true.
      */
     private static final String SELECTED_FALSE = "false"; //$NON-NLS-1$

     /**
      * Attribute status false.
      */
     private static final String SELECTED_TRUE = "true"; //$NON-NLS-1$

     /**
      * Constant for any element.
      */
     public static final int ON_ANY = 0;

     /**
      * Constant for any selected element only.
      */
     public static final int ON_SELECTED_ONLY = 1;

     /**
      * Constant for selected element and children.
      */
     public static final int ON_SELECTED_AND_CHILDREN = 2;

     /**
      * Constant for any element in same container.
      */
     public static final int ON_ANY_IN_SAME_CONTAINER = 3;

     /**
      * Constant for on working set.
      */
     public static final int ON_WORKING_SET = 4;

     static final int DEFAULT_ON_RESOURCE = ON_ANY;

     static final boolean DEFAULT_ACTIVATION_STATUS = true;

     protected List rootTypes = new ArrayList ();

     protected List selectedTypes = new ArrayList ();

     protected IWorkingSet workingSet;

     protected int onResource;

     protected boolean enabled;

     private IResource[] focusResource;

     private Set cachedWorkingSet;

     // The human readable name for the filter
 private String name;

     /**
      * Create a new instance of the receiver.
      *
      * @param filterName
      * The human readable name for the filter
      * @param rootTypes
      * The types this filter will be applied to
      */
     MarkerFilter(String filterName, String [] rootTypes) {

         name = filterName;

         for (int i = 0; i < rootTypes.length; i++) {
             MarkerType type = MarkerTypesModel.getInstance().getType(
                     rootTypes[i]);

             if (!this.rootTypes.contains(type)) {
                 this.rootTypes.add(type);
             }
         }
         resetState();
     }

     /**
      * List all types known to this MarkerFilter.
      *
      * @param types
      * list to be filled in with types
      */
     public void addAllSubTypes(List types) {
         for (int i = 0; i < rootTypes.size(); i++) {
             MarkerType rootType = (MarkerType) rootTypes.get(i);
             addAllSubTypes(types, rootType);
         }
     }

     private void addAllSubTypes(List types, MarkerType type) {
         if (type == null) {
             return;
         }

         if (!types.contains(type)) {
             types.add(type);
         }

         MarkerType[] subTypes = type.getSubtypes();

         for (int i = 0; i < subTypes.length; i++) {
             addAllSubTypes(types, subTypes[i]);
         }
     }

     /**
      * Adds all markers in the given set of resources to the given list
      *
      * @param resultList
      * @param resources
      * @param markerTypeId
      * @param depth
      * @throws CoreException
      */
     private List findMarkers(IResource[] resources, int depth, int limit,
             IProgressMonitor mon, boolean ignoreExceptions)
             throws CoreException {
         if (resources == null) {
             return Collections.EMPTY_LIST;
         }

         List resultList = new ArrayList (resources.length * 2);

         // Optimization: if a type appears in the selectedTypes list along with
 // all of its
 // subtypes, then combine these in a single search.

         // List of types that haven't been replaced by one of their supertypes
 HashSet typesToSearch = new HashSet (selectedTypes.size());

         // List of types that appeared in selectedTypes along with all of their
 // subtypes
 HashSet includeAllSubtypes = new HashSet (selectedTypes.size());

         typesToSearch.addAll(selectedTypes);

         Iterator iter = selectedTypes.iterator();

         while (iter.hasNext()) {
             MarkerType type = (MarkerType) iter.next();

             Collection subtypes = Arrays.asList(type.getAllSubTypes());

             if (selectedTypes.containsAll(subtypes)) {
                 typesToSearch.removeAll(subtypes);

                 includeAllSubtypes.add(type);
             }
         }

         mon.beginTask(MarkerMessages.MarkerFilter_searching, typesToSearch
                 .size()
                 * resources.length);

         // Use this hash set to determine if there are any resources in the
 // list that appear along with their parent.
 HashSet resourcesToSearch = new HashSet ();

         // Insert all the resources into the hashset
 for (int idx = 0; idx < resources.length; idx++) {
             IResource next = resources[idx];

             if (!next.exists()) {
                 continue;
             }

             if (resourcesToSearch.contains(next)) {
                 mon.worked(typesToSearch.size());
             } else {
                 resourcesToSearch.add(next);
             }
         }

         // Iterate through all the selected resources
 for (int resourceIdx = 0; resourceIdx < resources.length; resourceIdx++) {
             iter = typesToSearch.iterator();

             IResource resource = resources[resourceIdx];

             // Skip resources that don't exist
 if (!resource.isAccessible()) {
                 continue;
             }

             if (depth == IResource.DEPTH_INFINITE) {
                 // Determine if any parent of this resource is also in our
 // filter
 IResource parent = resource.getParent();
                 boolean found = false;
                 while (parent != null) {
                     if (resourcesToSearch.contains(parent)) {
                         found = true;
                     }

                     parent = parent.getParent();
                 }

                 // If a parent of this resource is also in the filter, we can
 // skip it
 // because we'll pick up its markers when we search the parent.
 if (found) {
                     continue;
                 }
             }

             // Iterate through all the marker types
 while (iter.hasNext()) {
                 MarkerType markerType = (MarkerType) iter.next();

                 // Only search for subtypes of the marker if we found all of its
 // subtypes in the filter criteria.
 IMarker[] markers = resource.findMarkers(markerType.getId(),
                         includeAllSubtypes.contains(markerType), depth);

                 mon.worked(1);

                 for (int idx = 0; idx < markers.length; idx++) {
                     ConcreteMarker marker;
                     try {
                         marker = MarkerList.createMarker(markers[idx]);
                     } catch (CoreException e) {
                         if (ignoreExceptions) {
                             continue;
                         }
                         throw e;

                     }

                     if (limit != -1 && resultList.size() >= limit) {
                         return resultList;
                     }

                     if (selectMarker(marker)) {
                         resultList.add(marker);
                     }
                 }
             }
         }

         mon.done();

         return resultList;
     }

     /**
      * Subclasses should override to determine if the given marker passes the
      * filter.
      *
      * @param marker
      * @return <code>true</code> if the marker passes the filter and
      * <code>false</code> otherwise
      */
     protected boolean selectMarker(ConcreteMarker marker) {
         return true;
     }

     /**
      * Searches the workspace for markers that pass this filter.
      *
      * @return Collection of markers.
      */
     Collection findMarkers(IProgressMonitor mon, boolean ignoreExceptions)
             throws CoreException {

         List unfiltered = Collections.EMPTY_LIST;

         if (!isEnabled()) {
             unfiltered = findMarkers(new IResource[] { ResourcesPlugin
                     .getWorkspace().getRoot() }, IResource.DEPTH_INFINITE, -1,
                     mon, ignoreExceptions);
         } else {
             // int limit = getFilterOnMarkerLimit() ? getMarkerLimit() + 1 : -1;
 int limit = -1;

             switch (getOnResource()) {
             case ON_ANY: {
                 unfiltered = findMarkers(new IResource[] { ResourcesPlugin
                         .getWorkspace().getRoot() }, IResource.DEPTH_INFINITE,
                         limit, mon, ignoreExceptions);
                 break;
             }
             case ON_SELECTED_ONLY: {
                 unfiltered = findMarkers(focusResource, IResource.DEPTH_ZERO,
                         limit, mon, ignoreExceptions);
                 break;
             }
             case ON_SELECTED_AND_CHILDREN: {
                 unfiltered = findMarkers(focusResource,
                         IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
                 break;
             }
             case ON_ANY_IN_SAME_CONTAINER: {
                 unfiltered = findMarkers(getProjects(focusResource),
                         IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
                 break;
             }
             case ON_WORKING_SET: {
                 unfiltered = findMarkers(getResourcesInWorkingSet(),
                         IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
             }
             }
         }

         if (unfiltered == null) {
             unfiltered = Collections.EMPTY_LIST;
         }

         return unfiltered;
     }

     /**
      * Return the resources in the working set. If it is empty then return the
      * workspace root.
      *
      * @return IResource[]
      */
     IResource[] getResourcesInWorkingSet() {
         if (workingSet == null) {
             return new IResource[0];
         }

         if (workingSet.isEmpty()) {
             return new IResource[] { ResourcesPlugin.getWorkspace().getRoot() };
         }

         IAdaptable[] elements = workingSet.getElements();
         List result = new ArrayList (elements.length);

         for (int idx = 0; idx < elements.length; idx++) {
             IResource next = (IResource) elements[idx]
                     .getAdapter(IResource.class);

             if (next != null) {
                 result.add(next);
             }
         }

         return (IResource[]) result.toArray(new IResource[result.size()]);
     }

     /**
      * Returns a set of strings representing the full pathnames to every
      * resource directly or indirectly contained in the working set. A resource
      * is in the working set iff its path name can be found in this set.
      *
      * @return Set
      */
     private Set getWorkingSetAsSetOfPaths() {
         if (cachedWorkingSet == null) {
             HashSet result = new HashSet ();

             addResourcesAndChildren(result, getResourcesInWorkingSet());

             cachedWorkingSet = result;
         }

         return cachedWorkingSet;
     }

     /***************************************************************************
      * Adds the paths of all resources in the given array to the given set.
      */
     private void addResourcesAndChildren(HashSet result, IResource[] resources) {
         for (int idx = 0; idx < resources.length; idx++) {

             IResource currentResource = resources[idx];

             result.add(currentResource.getFullPath().toString());

             if (currentResource instanceof IContainer) {
                 IContainer cont = (IContainer) currentResource;

                 try {
                     addResourcesAndChildren(result, cont.members());
                 } catch (CoreException e) {
                     // Ignore errors
 }
             }

         }
     }

     /**
      * Returns the set of projects that contain the given set of resources.
      *
      * @param resources
      * @return IProject[]
      */
     static IProject[] getProjects(IResource[] resources) {
         if (resources == null) {
             return new IProject[0];
         }

         Collection projects = getProjectsAsCollection(resources);

         return (IProject[]) projects.toArray(new IProject[projects.size()]);
     }

     /**
      * Return the projects for the elements.
      *
      * @param elements
      * collection of IResource or IResourceMapping
      * @return Collection of IProject
      */
     static Collection getProjectsAsCollection(Object [] elements) {
         HashSet projects = new HashSet ();

         for (int idx = 0; idx < elements.length; idx++) {
             if (elements[idx] instanceof IResource) {
                 projects.add(((IResource) elements[idx]).getProject());
             } else {
                 IProject[] mappingProjects = (((ResourceMapping) elements[idx])
                         .getProjects());
                 for (int i = 0; i < mappingProjects.length; i++) {
                     projects.add(mappingProjects[i]);
                 }
             }

         }

         return projects;
     }

     /**
      * Return whether or not the receiver would select the marker.
      *
      * @param marker
      * @return boolean
      */
     public boolean select(ConcreteMarker marker) {
         if (!isEnabled()) {
             return true;
         }

         return selectByType(marker) && selectBySelection(marker)
                 && selectMarker(marker);
     }

     private boolean selectByType(ConcreteMarker marker) {
         return selectedTypes.contains(MarkerTypesModel.getInstance().getType(
                 marker.getType()));
     }

     /**
      * Returns whether the specified marker should be filter out or not.
      *
      * @param marker
      * the marker to test
      * @return true=the marker should not be filtered out false=the marker
      * should be filtered out
      */
     private boolean selectBySelection(ConcreteMarker marker) {
         if (onResource == ON_ANY || marker == null) {
             return true;
         }

         if (focusResource == null) {
             return true;
         }

         IResource resource = marker.getResource();

         if (onResource == ON_WORKING_SET) {

             if (resource != null) {
                 return isEnclosed(resource);
             }

         } else if (onResource == ON_ANY_IN_SAME_CONTAINER) {
             IProject project = resource.getProject();

             if (project == null) {
                 return false;
             }

             for (int i = 0; i < focusResource.length; i++) {
                 IProject selectedProject = focusResource[i].getProject();

                 if (selectedProject == null) {
                     continue;
                 }

                 if (project.equals(selectedProject)) {
                     return true;
                 }
             }
         } else if (onResource == ON_SELECTED_ONLY) {
             for (int i = 0; i < focusResource.length; i++) {
                 if (resource.equals(focusResource[i])) {
                     return true;
                 }
             }
         } else if (onResource == ON_SELECTED_AND_CHILDREN) {
             for (int i = 0; i < focusResource.length; i++) {
                 IResource parentResource = resource;

                 while (parentResource != null) {
                     if (parentResource.equals(focusResource[i])) {
                         return true;
                     }

                     parentResource = parentResource.getParent();
                 }
             }
         }

         return false;
     }

     /**
      * Returns if the given resource is enclosed by a working set element.
      * Previous versions of this method used IContainmentAdapter for containment
      * tests. For performance reasons, this is no longer possible. Code that
      * relies on this behavior should be updated appropriately.
      *
      * @param element
      * resource to test for enclosure by a working set element
      * @return true if element is enclosed by a working set element and false
      * otherwise.
      */
     private boolean isEnclosed(IResource element) {
         if (workingSet == null) {
             return false;
         }

         if (workingSet.isEmpty()) {
             return true; // Everything is in an empty working set
 }
         Set workingSetPaths = getWorkingSetAsSetOfPaths();

         return workingSetPaths.contains(element.getFullPath().toString());
     }

     /**
      * <ul>
      * <li><code>MarkerFilter.ON_ANY</code> if showing items associated with
      * any resource.</li>
      * <li><code>MarkerFilter.ON_SELECTED_ONLY</code> if showing items
      * associated with the selected resource within the workbench.</li>
      * <li><code>MarkerFilter.ON_SELECTED_AND_CHILDREN</code> if showing
      * items associated with the selected resource within the workbench and its
      * children.</li>
      * <li><code>MarkerFilter.ON_ANY_OF_SAME_PROJECT</code> if showing items
      * in the same project as the selected resource within the workbench.</li>
      * <li><code>MarkerFilter.ON_WORKING_SET</code> if showing items in some
      * working set.</li>
      * </ul>
      *
      * @return int
      */
     public int getOnResource() {
         return onResource;
     }

     /**
      * Sets the type of filtering by selection.
      *
      * @param onResource
      * must be one of:
      * <ul>
      * <li><code>MarkerFilter.ON_ANY_RESOURCE</code></li>
      * <li><code>MarkerFilter.ON_SELECTED_RESOURCE_ONLY</code></li>
      * <li><code>MarkerFilter.ON_SELECTED_RESOURCE_AND_CHILDREN</code></li>
      * <li><code>MarkerFilter.ON_ANY_RESOURCE_OF_SAME_PROJECT</code></li>
      * <li><code>MarkerFilter.ON_WORKING_SET</code></li>
      * </ul>
      */
     void setOnResource(int onResource) {
         if (onResource >= ON_ANY && onResource <= ON_WORKING_SET) {
             this.onResource = onResource;
         }
     }

     /**
      * @return the selected resource(s) withing the workbench.
      */
     IResource[] getFocusResource() {
         return focusResource;
     }

     /**
      * Sets the focused resources.
      *
      * @param resources
      */
     public void setFocusResource(IResource[] resources) {
         focusResource = resources;
     }

     /**
      * @return
      * <ul>
      * <li><code>true</code> if the filter is enabled.</li>
      * <li><code>false</code> if the filter is not enabled.</li>
      * </ul>
      */
     public boolean isEnabled() {
         return enabled;
     }

     /**
      * <b>Warning:</b> for internal package use only. Return the root marker
      * types.
      *
      * @return the root marker types.
      */
     public List getRootTypes() {
         return rootTypes;
     }

     /**
      * <b>Warning:</b> for internal package use only. Return the selected
      * types.
      *
      * @return the selected marker types to be displayed.
      */
     public List getSelectedTypes() {
         return selectedTypes;
     }

     /**
      * Find the typeModel entry that matches id.
      *
      * @param id
      * the ID for a marker type
      * @return MarkerType or <code>null</code> if it is not found.
      */
     public MarkerType getMarkerType(String id) {
         return MarkerTypesModel.getInstance().getType(id);
     }

     /**
      * @return the current working set or <code>null</code> if no working set
      * is defined.
      */
     IWorkingSet getWorkingSet() {
         return workingSet;
     }

     /**
      * Sets the enablement state of the filter.
      */
     void setEnabled(boolean enabled) {
         this.enabled = enabled;
     }

     /**
      * Sets the current working set.
      */
     void setWorkingSet(IWorkingSet workingSet) {
         this.workingSet = workingSet;
         cachedWorkingSet = null;
     }

     /**
      * Reset to the default state.
      */
     void resetState() {
         enabled = DEFAULT_ACTIVATION_STATUS;
         onResource = DEFAULT_ON_RESOURCE;
         selectedTypes.clear();
         addAllSubTypes(selectedTypes);
         setWorkingSet(null);
     }

     /**
      * Restore the state in the memento.
      *
      * @param memento
      */
     public final void restoreState(IMemento memento) {
         resetState();
         restoreFilterSettings(memento);

     }

     /**
      * Restore the state of the receiver in the supplied settings. This is kept
      * for backwards compatibility with 3.1 dialog settings.
      *
      * @param settings
      */
     public void restoreFilterSettings(IDialogSettings settings) {

         resetState();

         String setting = settings.get(TAG_ENABLED);

         if (setting != null) {
             enabled = Boolean.valueOf(setting).booleanValue();
         }

         setting = settings.get(TAG_ON_RESOURCE);

         if (setting != null) {
             try {
                 onResource = Integer.parseInt(setting);
             } catch (NumberFormatException eNumberFormat) {
             }
         }

         // new selection list attribute
 // format is "id:(true|false):"
 setting = settings.get(TAG_SELECTION_STATUS);

         if (setting != null) {
             selectedTypes.clear();

             // get the complete list of types
 List newTypes = new ArrayList ();
             addAllSubTypes(newTypes);

             StringTokenizer stringTokenizer = new StringTokenizer (setting);

             while (stringTokenizer.hasMoreTokens()) {
                 String id = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
                 String status = null;
                 if (stringTokenizer.hasMoreTokens()) {
                     status = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
                 }

                 MarkerType markerType = MarkerTypesModel.getInstance().getType(
                         id);
                 if (markerType != null) {
                     newTypes.remove(markerType);

                     // add the type to the selected list
 if (!SELECTED_FALSE.equals(status)
                             && !selectedTypes.contains(markerType)) {
                         selectedTypes.add(markerType);
                     }
                 }
             }

             // any types we know about that weren't either true or
 // false in the selection attribute are new. By default,
 // new marker types will be selected=true
 for (int i = 0; i < newTypes.size(); ++i) {
                 selectedTypes.add(newTypes.get(i));
             }
         } else {
             // the settings didn't contain the new selection attribute
 // so check for the old selection attribute.
 // format is just "id:"
 setting = settings.get(TAG_SELECTED_TYPES);

             if (setting != null) {
                 generateSelectedTypes(setting);
             }

         }

         setting = settings.get(TAG_WORKING_SET);

         if (setting != null) {
             setWorkingSet(PlatformUI.getWorkbench().getWorkingSetManager()
                     .getWorkingSet(setting));
         }
     }

     /**
      * Set the selected types based on the value.
      *
      * @param selectedTypesValue
      */
     void generateSelectedTypes(String selectedTypesValue) {
         selectedTypes.clear();
         StringTokenizer stringTokenizer = new StringTokenizer (
                 selectedTypesValue);

         while (stringTokenizer.hasMoreTokens()) {
             MarkerType markerType = getMarkerType(stringTokenizer
                     .nextToken(TAG_TYPES_DELIMITER));

             if (markerType != null && !selectedTypes.contains(markerType)) {
                 selectedTypes.add(markerType);
             }
         }
     }

     /**
      * Find the markerType matching typeName
      *
      * @param typeName
      * @return MarkerType
      */
     MarkerType findMarkerType(String typeName) {
         return MarkerTypesModel.getInstance().getType(typeName);
     }

     /**
      * Restore the state of the receiver in the supplied settings.
      *
      * @param memento
      */
     protected void restoreFilterSettings(IMemento memento) {
         String setting = memento.getString(TAG_ENABLED);

         if (setting != null) {
             enabled = Boolean.valueOf(setting).booleanValue();
         }

         Integer resourceSetting = memento.getInteger(TAG_ON_RESOURCE);

         if (resourceSetting != null) {
             onResource = resourceSetting.intValue();
         }

         // new selection list attribute
 // format is "id:(true|false):"
 setting = memento.getString(TAG_SELECTION_STATUS);

         if (setting != null) {
             selectedTypes.clear();

             // get the complete list of types
 List newTypes = new ArrayList ();
             addAllSubTypes(newTypes);

             StringTokenizer stringTokenizer = new StringTokenizer (setting);

             while (stringTokenizer.hasMoreTokens()) {
                 String id = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
                 String status = null;
                 if (stringTokenizer.hasMoreTokens()) {
                     status = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
                 }

                 MarkerType markerType = MarkerTypesModel.getInstance().getType(
                         id);
                 if (markerType != null) {
                     newTypes.remove(markerType);

                     // add the type to the selected list
 if (!SELECTED_FALSE.equals(status)
                             && !selectedTypes.contains(markerType)) {
                         selectedTypes.add(markerType);
                     }
                 }
             }

             // any types we know about that weren't either true or
 // false in the selection attribute are new. By default,
 // new marker types will be selected=true
 for (int i = 0; i < newTypes.size(); ++i) {
                 selectedTypes.add(newTypes.get(i));
             }
         } else {
             // the settings didn't contain the new selection attribute
 // so check for the old selection attribute.
 // format is just "id:"
 setting = memento.getString(TAG_SELECTED_TYPES);

             if (setting != null) {
                 generateSelectedTypes(setting);
             }

         }

         setting = memento.getString(TAG_WORKING_SET);

         if (setting != null) {
             setWorkingSet(PlatformUI.getWorkbench().getWorkingSetManager()
                     .getWorkingSet(setting));
         }
     }

     /**
      * Save the filter settings for the receiver.
      *
      * @param settings
      */
     public void saveFilterSettings(IMemento settings) {

         settings.putString(TAG_ENABLED, String.valueOf(enabled));
         settings.putInteger(TAG_ON_RESOURCE, onResource);

         String markerTypeIds = ""; //$NON-NLS-1$

         List includedTypes = new ArrayList ();
         addAllSubTypes(includedTypes);
         for (int i = 0; i < includedTypes.size(); i++) {
             MarkerType markerType = (MarkerType) includedTypes.get(i);
             markerTypeIds += markerType.getId() + TAG_TYPES_DELIMITER;
             if (selectedTypes.contains(markerType)) {
                 markerTypeIds += SELECTED_TRUE + TAG_TYPES_DELIMITER;
             } else {
                 markerTypeIds += SELECTED_FALSE + TAG_TYPES_DELIMITER;
             }
         }

         settings.putString(TAG_SELECTION_STATUS, markerTypeIds);

         if (workingSet != null) {
             settings.putString(TAG_WORKING_SET, workingSet.getName());
         }
     }

     /**
      * Get the name of the receiver
      *
      * @return String
      */
     public String getName() {
         return name;
     }

     /**
      * Make a clone of the receiver.
      *
      * @return MarkerFilter
      * @throws CloneNotSupportedException
      */
     public MarkerFilter makeClone() throws CloneNotSupportedException {
         return (MarkerFilter) this.clone();
     }

     /**
      * Set the selected types.
      *
      * @param selectedTypes
      * List of MarkerType.
      */
     public void setSelectedTypes(List selectedTypes) {
         this.selectedTypes = selectedTypes;
     }

 }

